//+build linux

package file

import (
	"os"
	"reflect"

	"github.com/asim/go-micro/v3/config/source"
	"github.com/fsnotify/fsnotify"
)

type watcher struct {
	f *file

	fw   *fsnotify.Watcher
	exit chan bool
}

func newWatcher(f *file) (source.Watcher, error) {
	fw, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	fw.Add(f.path)

	return &watcher{
		f:    f,
		fw:   fw,
		exit: make(chan bool),
	}, nil
}

func (w *watcher) Next() (*source.ChangeSet, error) {
	// is it closed?
	select {
	case <-w.exit:
		return nil, source.ErrWatcherStopped
	default:
	}

	for {
		// try get the event
		select {
		case event, _ := <-w.fw.Events:
			if event.Op == fsnotify.Rename {
				// check existence of file, and add watch again
				_, err := os.Stat(event.Name)
				if err == nil || os.IsExist(err) {
					w.fw.Add(event.Name)
				}
			}

			c, err := w.f.Read()
			if err != nil {
				return nil, err
			}

			// ARCH: Linux centos-7.shared 3.10.0-693.5.2.el7.x86_64
			// Sometimes, ioutil.WriteFile triggers multiple fsnotify.Write events, which may be a bug.

			// Detect if the file has changed
			if reflect.DeepEqual(c.Data, w.f.data) {
				continue
			}
			w.f.data = c.Data

			return c, nil
		case err := <-w.fw.Errors:
			return nil, err
		case <-w.exit:
			return nil, source.ErrWatcherStopped
		}
	}
}

func (w *watcher) Stop() error {
	return w.fw.Close()
}
